# -*- coding:utf-8 -*-
# title           :
# description     :
# author          :Python超人/三月三提供
# date            :2024-06-01
# link            :https://gitcode.net/pythoncr/
# python_version  :3.9
# ==============================================================================
import os
import svgwrite

from 神笔码靓.神笔库 import 打印红色消息
from 神笔码靓.神笔库.定义集 import *
from 神笔码靓.神笔库.元素库 import 可变换元素, 元素位置信息, 样式可修改元素
from 神笔码靓.神笔库.神笔绘图板工具 import 神笔绘图板工具


class 路径元素(可变换元素, 样式可修改元素):
    _路径元素列表 = []

    def __init__(self, 高清图, 所属组: list):
        super(路径元素, self).__init__(高清图, 所属组)
        self.前路径: 路径元素 = None
        self.后路径: 路径元素 = None
        self._路径元素列表.append(self)
        self._批量坐标 = []
        self._点辅助全局开 = True
        self._点辅助字典 = {}

    def 点辅助记录点(self, 记录点ID):
        if 记录点ID in self._点辅助字典:
            return self._点辅助字典[记录点ID]
        return None

    def 寻找路径(self, 索引=None, id_=None):
        from 神笔码靓.神笔库.元素库 import 路径元素
        路径元素_: 路径元素 = self
        if 索引 is None and id_ is None:
            return 路径元素_
        elif id_ is not None:
            for 路径元素 in self._路径元素列表:
                if 路径元素.获取属性值("id_") == id_:
                    路径元素_ = 路径元素
                    break
        elif 索引 is not None:
            路径元素_ = self._路径元素列表[索引]
        return 路径元素_

    def 落笔(self, x, y=None):
        self.移动到(x, y)
        return self

    def 移动到(self, x, y=None):
        if y is None:
            x, y = x
        self.追加顶点(("M", x, y))
        self._记录当前线段数据(x1=x - 1, y1=y, x2=x, y2=y)
        return self

    # region 批量生成线
    def 坐标(self, x, y=None):
        if y is None or isinstance(x, tuple):
            self._批量坐标.append(x)
        elif x is not None and y is not None:
            self._批量坐标.append((x, y))
        else:
            raise Exception(f"坐标的数据不正确：({x},{y})")

        return self

    def 坐标连直线(self):
        if len(self._批量坐标) == 0:
            return self
        for _坐标 in self._批量坐标:
            x, y = _坐标
            self.画直线(终点x=x, 终点y=y)
        self._批量坐标 = []
        return self

    # def 坐标连曲线(self):
    #     if len(self._批量坐标) == 0:
    #         return self
    #     for _坐标 in self._批量坐标:
    #         x, y = _坐标
    #
    #     self._批量坐标 = []
    #     return self

    # endregion

    # region 显示点坐标
    def _获取终点的坐标(self, 点):
        命令 = 点[0]
        if 命令 in ["M", "L", "T"]:
            return 点[1], 点[2]
        elif 命令 == "H":
            c, x = 点
            return x, None
        elif 命令 == "V":
            c, y = 点
            return None, y
        elif 命令 == "C":
            c, x1, y1, x2, y2, x, y = 点
            return x, y
        elif 命令 == "S":
            c, 终点控制点x, 终点控制点y, 终点x, 终点y = 点
            return 终点x, 终点y
        elif 命令 == "Q":
            c, x1, y1, x, y = 点
            return x, y
        elif 命令 == "A":
            c, rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, x, y = 点
            return x, y
        elif 命令 in ["z", "Z"]:
            return 0, 0
        else:
            return 0, 0

    def _坐标数(self, 坐标, 保留小数=2):
        return str(round(坐标[0], 保留小数)).rstrip("0").rstrip(".") + ", " + \
               str(round(坐标[1], 保留小数)).rstrip("0").rstrip(".")

    def _显示坐标标签(self, 坐标, 坐标偏移=None, 定位类型=定位点类型.实圆):
        x, y = 坐标
        坐标文字 = self._坐标数(坐标)
        if 坐标偏移 is None:
            坐标偏移x, 坐标偏移y = 0, 0
        else:
            坐标偏移x, 坐标偏移y = 坐标偏移
        self.高清图.显示坐标(坐标, 坐标1文字偏移=坐标偏移, 定位类型=定位类型)
        # self.高清图.写字(x=x + 坐标偏移x, y=y + 坐标偏移y + 15, 文字=坐标文字)

    def _倒序查找XY(self, 点列表):
        x, y = None, None
        for i in range(-2, -len(点列表) - 1, -1):
            倒数第二点 = 点列表[i]
            x_, y_ = self._获取终点的坐标(倒数第二点)
            if x is None:
                x = x_
            if y is None:
                y = y_
            if x is None or y is None:
                continue
            else:
                break
        return x, y

    def _连辅助线(self, x1, y1, x, y):
        self.高清图.连直线(路径点列表=[(x, y), (x1, y1)], 描边颜色=self.高清图.辅助线颜色, 描边样式="3,3")

    def _显示点坐标_H(self, 最后点, 点列表, 坐标=True, 坐标偏移=None, 辅助线=False, 记录点ID=None):
        y = None
        for i in range(-2, -len(点列表), -1):
            倒数第二点 = 点列表[i]
            x, y = self._获取终点的坐标(倒数第二点)
            if y is not None:
                break

        c, x = 最后点
        if 记录点ID:
            self._点辅助字典[记录点ID] = (x, y)
        if self._点辅助全局开:
            self._显示坐标标签(坐标=(x, y), 坐标偏移=坐标偏移)

    def _显示点坐标_L(self, 最后点, 坐标=True, 坐标偏移=None, 辅助线=False, 记录点ID=None):
        c, x, y = 最后点
        if 记录点ID:
            self._点辅助字典[记录点ID] = (x, y)
        if self._点辅助全局开:
            self._显示坐标标签(坐标=(x, y), 坐标偏移=坐标偏移)

    def _显示点坐标_V(self, 最后点, 点列表, 坐标=True, 坐标偏移=None, 辅助线=False, 记录点ID=None):
        x = None
        for i in range(-2, -len(点列表), -1):
            倒数第二点 = 点列表[i]
            x, y = self._获取终点的坐标(倒数第二点)
            if x is not None:
                break

        c, y = 最后点
        if 记录点ID:
            self._点辅助字典[记录点ID] = (x, y)
        if self._点辅助全局开:
            self._显示坐标标签(坐标=(x, y), 坐标偏移=坐标偏移)

    def _显示点坐标_C(self, 最后点, 点列表, 坐标=True, 坐标偏移=None, 辅助线=False, 记录点ID=None):
        x3, y3 = self._倒序查找XY(点列表)
        c, x1, y1, x2, y2, x, y = 最后点
        if self._点辅助全局开:
            self._显示坐标标签(坐标=(x, y), 坐标偏移=坐标偏移)
            self._显示坐标标签(坐标=(x1, y1), 坐标偏移=坐标偏移, 定位类型=定位点类型.空方)
            self._显示坐标标签(坐标=(x2, y2), 坐标偏移=坐标偏移, 定位类型=定位点类型.实方)
            self._连辅助线(x1, y1, x3, y3)
            self._连辅助线(x2, y2, x, y)
        if 记录点ID:
            self._点辅助字典[记录点ID] = (x, y)

    def _显示点坐标_S(self, 最后点, 坐标=True, 坐标偏移=None, 辅助线=False, 记录点ID=None):
        c, x1, y1, x, y = 最后点
        if self._点辅助全局开:
            self._显示坐标标签(坐标=(x, y), 坐标偏移=坐标偏移)
            self._连辅助线(x1, y1, x, y)
            self._显示坐标标签(坐标=(x1, y1), 坐标偏移=坐标偏移, 定位类型=定位点类型.实方)
        if 记录点ID:
            self._点辅助字典[记录点ID] = (x, y)

    def _显示点坐标_Q(self, 最后点, 坐标=True, 坐标偏移=None, 辅助线=False, 记录点ID=None):
        c, x1, y1, x, y = 最后点
        if self._点辅助全局开:
            self._显示坐标标签(坐标=(x, y), 坐标偏移=坐标偏移)
            self._连辅助线(x1, y1, x, y)
            self._显示坐标标签(坐标=(x1, y1), 坐标偏移=坐标偏移, 定位类型=定位点类型.实方)
        if 记录点ID:
            self._点辅助字典[记录点ID] = (x, y)

    def _显示点坐标_T(self, 最后点, 点列表, 坐标=True, 坐标偏移=None, 辅助线=False, 记录点ID=None):
        x1, y1 = self._倒序查找XY(点列表)
        c, x, y = 最后点
        if self._点辅助全局开:
            self._显示坐标标签(坐标=(x, y), 坐标偏移=坐标偏移)
            self._显示坐标标签(坐标=(x1, y1), 坐标偏移=坐标偏移)
        if 记录点ID:
            self._点辅助字典[记录点ID] = (x, y)

    def _显示点坐标_A(self, 最后点, 坐标=True, 坐标偏移=None, 辅助线=False, 记录点ID=None):
        c, rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, x, y = 最后点
        if self._点辅助全局开:
            self._显示坐标标签(坐标=(x, y), 坐标偏移=坐标偏移)
        if 记录点ID:
            self._点辅助字典[记录点ID] = (x, y)

    def 点辅助开关(self, 开):
        self._点辅助全局开 = 开
        return self

    def 点辅助(self, 开启=True, 坐标=True, 坐标偏移=None, 辅助线=False, 记录点ID=None):
        if not 开启 or (not self._点辅助全局开 and 记录点ID is None):
            return self
        P = self.获取属性值
        点列表 = P("点列表")
        if 点列表 is None:
            return self
        if len(点列表) == 0:
            return self

        点列表 = 神笔绘图板工具.转SVG绘制路径命令(点列表)

        最后点 = 点列表[-1]

        命令 = 最后点[0]
        # ['M', 'm', 'L', 'l', 'H', 'h', 'V', 'v', 'C', 'c', 'S', 's', 'Q', 'q', 'T', 't', 'A', 'a', 'Z', 'z']
        if 命令 in ["M", "L"]:
            self._显示点坐标_L(最后点, 坐标=坐标, 坐标偏移=坐标偏移, 辅助线=辅助线, 记录点ID=记录点ID)
        elif 命令 == "H":
            倒数第二点 = 点列表[-2]
            self._显示点坐标_H(最后点, 点列表, 坐标=坐标, 坐标偏移=坐标偏移, 辅助线=辅助线, 记录点ID=记录点ID)
        elif 命令 == "V":
            倒数第二点 = 点列表[-2]
            self._显示点坐标_V(最后点, 点列表, 坐标=坐标, 坐标偏移=坐标偏移, 辅助线=辅助线, 记录点ID=记录点ID)
        elif 命令 == "C":
            倒数第二点 = 点列表[-2]
            self._显示点坐标_C(最后点, 点列表, 坐标=坐标, 坐标偏移=坐标偏移, 辅助线=辅助线, 记录点ID=记录点ID)
        elif 命令 == "S":
            self._显示点坐标_S(最后点, 坐标=坐标, 坐标偏移=坐标偏移, 辅助线=辅助线, 记录点ID=记录点ID)
        elif 命令 == "Q":
            self._显示点坐标_Q(最后点, 坐标=坐标, 坐标偏移=坐标偏移, 辅助线=辅助线, 记录点ID=记录点ID)
        elif 命令 == "T":
            倒数第二点 = 点列表[-2]
            self._显示点坐标_T(最后点, 点列表, 坐标=坐标, 坐标偏移=坐标偏移, 辅助线=辅助线, 记录点ID=记录点ID)
        elif 命令 == "A":
            self._显示点坐标_A(最后点, 坐标=坐标, 坐标偏移=坐标偏移, 辅助线=辅助线, 记录点ID=记录点ID)
        elif 命令 in ["z", "Z"]:
            pass
        else:
            打印红色消息(f"无法显示点坐标:{最后点}")

        return self

    # endregion

    def 闭合(self):
        self.追加顶点(("闭合",))
        # self._清除当前线段数据()
        self._记录当前线段数据(x1=-1, y1=0, x2=0, y2=0)
        return self

    def _获取当前线段数据(self):
        if hasattr(self, "_当前线段数据"):
            return self._当前线段数据
        return None

    # def _清除当前线段数据(self):
    #     if hasattr(self, "_当前线段数据"):
    #         delattr(self, "_当前线段数据")

    def _计算右转直线坐标(self, x1, y1, x2, y2, angle, length):
        """
        根据已知线段的端点坐标，计算偏转一定角度和长度后的新坐标点。

        参数:
        x1 (float): 线段起点的x坐标
        y1 (float): 线段起点的y坐标
        x2 (float): 线段终点的x坐标
        y2 (float): 线段终点的y坐标
        angle (float): 偏转角度，以弧度制表示
        length (float): 直线长度

        返回:
        tuple: 包含新坐标点的x坐标和y坐标的元组 (x3, y3)
        """
        # 计算原线段的长度
        original_length = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
        # 计算原线段与x轴正方向的夹角（弧度制）
        original_angle = math.atan2(y2 - y1, x2 - x1)
        # 计算偏转后的角度
        new_angle = original_angle + angle * math.pi / 180
        # 计算新坐标点的x坐标
        x3 = x2 + length * math.cos(new_angle)
        # 计算新坐标点的y坐标
        y3 = y2 + length * math.sin(new_angle)
        return x3, y3

    def 右转直线(self, 右转角度, 直线长度):
        _当前线段数据 = self._获取当前线段数据()
        if _当前线段数据 is None:
            raise Exception("上一条必须是直线")
        x1, y1, x2, y2 = _当前线段数据
        x3, y3 = self._计算右转直线坐标(x1, y1, x2, y2, 右转角度, 直线长度)

        # A = x1, y1
        # B = x2, y2
        #
        # self.高清图.定位(A, B)

        self.画直线(x3, y3)
        return self

    def 左转直线(self, 左转角度, 直线长度):
        self.右转直线(-左转角度, 直线长度)
        return self

    def 前进直线(self, 直线长度):
        self.右转直线(0, 直线长度)
        return self

    def 后退直线(self, 直线长度):
        self.右转直线(0, -直线长度)
        return self

    def _获取上一个点的数据(self):
        P = self.获取属性值
        点列表 = P("点列表")
        if 点列表 is None:
            return None
        if len(点列表) == 0:
            return None

        点列表 = 神笔绘图板工具.转SVG绘制路径命令(点列表)

        点x, 点y = None, None
        for i in range(len(点列表) - 1, -1, -1):
            if not (点x is None and 点y is None):
                break
            点 = 点列表[i]
            # # ['M', 'm', 'L', 'l', 'H', 'h', 'V', 'v', 'C', 'c', 'S', 's', 'Q', 'q', 'T', 't', 'A', 'a', 'Z', 'z']
            #
            if 点[0] in ["M", "L", "T"]:
                return 点[1], 点[2]
            # elif 点[0] == "L":
            #     return 点[1], 点[2]
            elif 点[0] == "H":
                点x = 点[1]
            elif 点[0] == "V":
                点y = 点[1]
            elif 点[0] == "S":
                # c, x1, y1, x, y
                return 点[3], 点[4]
            elif 点[0] == "C":
                # c, x1, y1, x2, y2, x, y
                return 点[5], 点[6]
            elif 点[0] == "Q":
                # c, x1, y1, x, y
                return 点[3], 点[4]
            # elif 点[0] == "T":
            #     # c, x, y
            #     return 点[1], 点[2]
            elif 点[0] == "A":
                # c, rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, x, y
                return 点[6], 点[7]
            else:
                打印红色消息(f"上一个点的数据：{点}")
                return None

        if 点x is None and 点y is None:
            return None
        return 点x, 点y

    # endregion

    # region 新路径

    def _新路径设置属性(self, 新路径):
        """
        @return:
        """
        描边宽度 = self.获取属性值("描边宽度", 1)
        新路径.描边宽度(描边宽度)
        填充颜色 = self.获取属性值("填充颜色", None)
        if 填充颜色 is not None:
            新路径.填充颜色(填充颜色)
        描边颜色 = self.获取属性值("描边颜色", None)
        if 描边颜色 is not None:
            新路径.描边颜色(描边颜色)
        透明度 = self.获取属性值("透明度", None)
        if 透明度 is not None:
            新路径.透明度(透明度)
        描边样式 = self.获取属性值("描边样式", None)
        if 描边样式 is not None:
            新路径.描边样式(描边样式)

    def 新路径(self, x=None, y=None, 描边颜色=None, 填充颜色=None, 描边宽度=None,
            透明度=None, 描边样式: str = None, id_: str = None):
        """

        @return:
        """
        上一个点的数据 = self._获取上一个点的数据()
        if 上一个点的数据 is None:
            x_, y_ = 0, 0
        else:
            x_, y_ = 上一个点的数据

        if x is None:
            x = x_
        if y is None:
            y = y_

        新路径 = 路径元素(self.高清图, self.所属组)
        新路径.设置属性值("点列表", [])
        if x is None and y is None:
            新路径.移动到(0, 0)
        elif x is None:
            新路径.移动到(0, y)
        elif y is None:
            新路径.移动到(x, 0)
        else:
            新路径.移动到(x, y)
        self._新路径设置属性(新路径)

        if 描边宽度 is not None:
            新路径.描边宽度(描边宽度)
        if 填充颜色 is not None:
            新路径.填充颜色(填充颜色)
        if 描边颜色 is not None:
            新路径.描边颜色(描边颜色)
        if 透明度 is not None:
            新路径.透明度(透明度)
        if 描边样式 is not None:
            新路径.描边样式(描边样式)
        if id_ is not None:
            新路径.设置属性值("id_", id_)

        新路径.前路径 = self
        self.后路径 = 新路径
        return 新路径

    def 新描边颜色(self, 颜色值, id_: str = None):
        新路径 = self.新路径()
        新路径.设置属性值(属性名="描边颜色", 属性值=颜色值)
        if id_ is not None:
            新路径.设置属性值("id_", id_)
        return 新路径

    def 新描边宽度(self, 宽度值, id_: str = None):
        新路径 = self.新路径()
        新路径.设置属性值(属性名="描边宽度", 属性值=宽度值)
        if id_ is not None:
            新路径.设置属性值("id_", id_)
        return 新路径

    def 新描边样式(self, 样式值, id_: str = None):
        新路径 = self.新路径()
        新路径.设置属性值(属性名="描边样式", 属性值=样式值)
        if id_ is not None:
            新路径.设置属性值("id_", id_)
        return 新路径

    # endregion

    def 画直线(self, 终点x, 终点y=None):
        if 终点y is None:
            终点x, 终点y = 终点x
        上一个点 = self._获取上一个点的数据()
        if 上一个点 is not None:
            x1, y1 = 上一个点
            self._记录当前线段数据(x1=x1, y1=y1, x2=终点x, y2=终点y)
        self.追加顶点(("L", 终点x, 终点y))
        return self

    # region 旋转直线
    def _记录当前线段数据(self, x1=None, y1=None, x2=None, y2=None):
        if hasattr(self, "_当前线段数据"):
            if x1 is not None:
                self._当前线段数据[0] = x1
            if y1 is not None:
                self._当前线段数据[1] = y1
            if x2 is not None:
                self._当前线段数据[2] = x2
            if y2 is not None:
                self._当前线段数据[3] = y2
        else:
            self._当前线段数据 = [x1, y1, x2, y2]

    def 画水平线(self, 终点x):
        上一个点 = self._获取上一个点的数据()
        if 上一个点 is not None:
            上一个点x, 上一个点y = 上一个点
            x1, y1, x2, y2 = 上一个点x, 上一个点y, 终点x, 上一个点y
            self._记录当前线段数据(x1, y1, x2, y2)
        self.追加顶点(("H", 终点x))
        return self

    def 画垂直线(self, 终点y):
        上一个点 = self._获取上一个点的数据()
        if 上一个点 is not None:
            上一个点x, 上一个点y = 上一个点
            x1, y1, x2, y2 = 上一个点x, 上一个点y, 上一个点x, 终点y
            self._记录当前线段数据(x1, y1, x2, y2)
        self.追加顶点(("V", 终点y))
        return self

    def 画曲线(self, 开始控制点: tuple[float, float], 终点控制点: tuple[float, float], 终点: tuple[float, float]):
        开始控制点x, 开始控制点y = 开始控制点
        终点控制点x, 终点控制点y = 终点控制点
        终点x, 终点y = 终点
        # self._清除当前线段数据()
        self.追加顶点(("C", 开始控制点x, 开始控制点y, 终点控制点x, 终点控制点y, 终点x, 终点y))
        self._记录当前线段数据(x1=终点控制点x, y1=终点控制点y, x2=终点x, y2=终点y)
        return self

    def 三次贝塞尔曲线(self, 开始控制点: tuple[float, float], 终点控制点: tuple[float, float], 终点: tuple[float, float]):
        开始控制点x, 开始控制点y = 开始控制点
        终点控制点x, 终点控制点y = 终点控制点
        终点x, 终点y = 终点
        # self._清除当前线段数据()
        self.追加顶点(("C", 开始控制点x, 开始控制点y, 终点控制点x, 终点控制点y, 终点x, 终点y))
        self._记录当前线段数据(x1=终点控制点x, y1=终点控制点y, x2=终点x, y2=终点y)
        return self

    def 平滑曲线(self, 终点控制点: tuple[float, float], 终点: tuple[float, float]):
        终点x, 终点y = 终点
        终点控制点x, 终点控制点y = 终点控制点
        # self._清除当前线段数据()
        self.追加顶点(("S", 终点控制点x, 终点控制点y, 终点x, 终点y))
        self._记录当前线段数据(x1=终点控制点x, y1=终点控制点y, x2=终点x, y2=终点y)
        return self

    def 二次贝塞尔曲线(self, 曲线控制点: tuple[float, float], 终点: tuple[float, float]):
        终点x, 终点y = 终点
        曲线控制点x, 曲线控制点y = 曲线控制点
        # self._清除当前线段数据()
        self.追加顶点(("Q", 曲线控制点x, 曲线控制点y, 终点x, 终点y))
        self._记录当前线段数据(x1=曲线控制点x, y1=曲线控制点y, x2=终点x, y2=终点y)
        return self

    def 平滑的二次贝塞尔曲线(self, 终点: tuple[float, float]):
        终点x, 终点y = 终点
        上一个点 = self._获取上一个点的数据()
        if 上一个点 is None:
            x1 = 终点x - 1
            y1 = 终点y
        else:
            x1, y1 = 上一个点
        # self._清除当前线段数据()
        self.追加顶点(("T", 终点x, 终点y))
        self._记录当前线段数据(x1=x1, y1=y1, x2=终点x, y2=终点y)
        return self

    import math

    def calculate_slope_of_elliptical_arc_point(self, x1, y1, x2, y2, rx, ry, x_axis_rotation, large_arc_flag,
                                                sweep_flag, x,
                                                y):
        """
        计算使用A命令绘制的椭圆弧上某点的斜率。

        参数:
        x1 (float): A命令椭圆弧起始点的x坐标
        y1 (float): A命令椭圆弧起始点的y坐标
        x2 (float): A命令椭圆弧终点的x坐标
        y2 (float): A命令椭圆弧终点的y坐标
        rx (float): 椭圆弧所在椭圆的x轴半径
        ry (float): 椭圆弧所在椭圆的y轴半径
        x_axis_rotation (float): A命令中的x - axis - rotation参数，旋转角度
        large_arc_flag (int): A命令中的large - arc - flag参数，大弧标志（0或1）
        sweep_flag (int): A命令中的sweep - flag参数，绘制方向标志（0或1）
        x (float): 椭圆弧上要计算斜率的点的x坐标
        y (float): 椭圆弧上要计算斜率的点的y坐标

        返回:
        float: 椭圆弧上该点的斜率，如果无法计算（例如在垂直切线情况下）返回None
        """
        import math

        # 将角度转换为弧度
        angle = math.radians(x_axis_rotation)
        cos_angle = math.cos(angle)
        sin_angle = math.sin(angle)

        # 步骤1：将终点坐标转换到未旋转的椭圆坐标系
        dx = (x1 - x2) / 2
        dy = (y1 - y2) / 2
        x1_ = cos_angle * dx + sin_angle * dy
        y1_ = -sin_angle * dx + cos_angle * dy

        # 确保半径足够大
        rx = abs(rx)
        ry = abs(ry)
        rx_sq = rx * rx
        ry_sq = ry * ry
        x1_sq = x1_ * x1_
        y1_sq = y1_ * y1_

        # 计算椭圆中心
        radical = (rx_sq * ry_sq - rx_sq * y1_sq - ry_sq * x1_sq) / (rx_sq * y1_sq + ry_sq * x1_sq)
        if radical < 0:
            radical = 0
        radical = math.sqrt(radical)

        if large_arc_flag == sweep_flag:
            radical = -radical

        cx_ = radical * rx * y1_ / ry
        cy_ = -radical * ry * x1_ / rx

        # 将终点转换回原始坐标系
        cx = cos_angle * cx_ - sin_angle * cy_ + (x1 + x2) / 2
        cy = sin_angle * cx_ + cos_angle * cy_ + (y1 + y2) / 2

        # 计算终点相对于椭圆中心的角度
        angle_x = (x2 - cx) * cos_angle + (y2 - cy) * sin_angle
        angle_y = -(x2 - cx) * sin_angle + (y2 - cy) * cos_angle

        # 计算斜率
        try:
            slope = (-rx * angle_y) / (ry * angle_x)
            return slope
        except ZeroDivisionError:
            return None

    def 画椭圆弧(self, x圆弧半径, y圆弧半径, 终点: tuple[float, float], 圆弧方向=0):
        # "椭圆弧线": "A",  # (rx, ry), x_axis_rotation, (large_arc_flag, sweep_flag), (x, y)
        终点x, 终点y = 终点
        if 圆弧方向 != 0:
            圆弧方向 = 1
        # self._清除当前线段数据()
        self.追加顶点(("A", x圆弧半径, y圆弧半径, 0, 0, 圆弧方向, 终点x, 终点y))
        last_x, last_y = self.获取上一个点的位置()
        slope = self.calculate_slope_of_elliptical_arc_point(x1=last_x, y1=last_y, x2=终点x, y2=终点y, rx=x圆弧半径, ry=y圆弧半径,
                                                             x_axis_rotation=0, large_arc_flag=0, sweep_flag=0, x=终点x,
                                                             y=终点y)
        self._记录当前线段数据(x1=终点x - 1, y1=终点y, x2=终点x, y2=终点y)
        return self

    def 画圆弧(self, 圆弧半径, 终点: tuple[float, float], 圆弧方向=0):
        终点x, 终点y = 终点
        if 圆弧方向 != 0:
            圆弧方向 = 1
        # 起点x, 起点y = self.获取上一个点的位置()
        # self._清除当前线段数据()
        self.追加顶点(("圆弧线", 圆弧半径, 圆弧方向, 终点x, 终点y))
        # last_x, last_y = self.获取上一个点的位置()
        # slope = self.calculate_slope_of_elliptical_arc_point(x1=last_x,y1=last_y,x2=终点x,y2=终点y,rx=圆弧半径,ry=圆弧半径,
        #                                                      x_axis_rotation=0,large_arc_flag=0, sweep_flag=0,x=终点x,y=终点y)
        # 斜率k可以表示为：k = (y2 - y1) / (x2 - x1)
        # slope = (终点y - 起点y) / (终点x - 起点x)
        # slope * (终点x - 起点x) - 终点y =  - 起点y
        # last_x, last_y = self.获取上一个点的位置()
        # # 起点x,起点y = last_x, last_y
        # 起点x = 终点x + 10
        # 起点y = 终点y - slope * (终点x - 起点x)
        # print(slope)
        self._记录当前线段数据(x1=终点x - 1, y1=终点y, x2=终点x, y2=终点y)
        return self

    def 画圆(self, 圆心: tuple[float, float], 圆方向=0, 起点=None):
        圆心x, 圆心y = 圆心
        if 圆方向 != 0:
            圆方向 = 1
        if 起点 is None:
            起点x, 起点y = self.获取上一个点的位置()
        else:
            起点x, 起点y = 起点
        点列表 = self.获取属性值("点列表", [])
        if len(点列表) == 0:
            self.移动到(起点x, 起点y)
        else:
            上起点x, 上起点y = self.获取上一个点的位置()
            if 上起点x != 起点x or 上起点y != 起点y:
                self.画直线(起点x, 起点y)

        终点x, 终点y = 2 * 圆心x - 起点x, 2 * 圆心y - 起点y
        圆半径 = math.sqrt((圆心x - 起点x) ** 2 + (圆心y - 起点y) ** 2)
        # self.追加顶点(("圆弧线", 圆半径, 圆方向, 终点x, 终点y))
        # self.追加顶点(("圆弧线", 圆半径, 圆方向, 起点x, 起点y))

        self.画圆弧(圆弧半径=圆半径, 终点=(终点x, 终点y), 圆弧方向=圆方向)
        self.画圆弧(圆弧半径=圆半径, 终点=(起点x, 起点y), 圆弧方向=圆方向)
        # self._清除当前线段数据()
        # self._记录当前线段数据(x1=终点x - 1, y1=终点y, x2=终点x, y2=终点y)
        return self

    def 路径缩放(self, x=1, y=1):
        """

        @param x:
        @param y:
        @return:
        """
        P = self.获取属性值
        点列表 = P("点列表")

        路径点列表 = []
        from 神笔码靓.神笔库 import 神笔绘图板工具
        点列表 = 神笔绘图板工具.转SVG绘制路径命令(点列表)

        # ['M', 'm', 'L', 'l', 'H', 'h', 'V', 'v', 'C', 'c', 'S', 's', 'Q', 'q', 'T', 't', 'A', 'a', 'Z', 'z']
        for 点 in 点列表:
            if 点[0] in ["M", "m", "L", "l", "T", "t"]:
                # 顶点x, 顶点y = 点[1]*x, 点[2]*y
                路径点列表.append((点[0], 点[1] * x, 点[2] * y))
            elif 点[0] in ["C", "c"]:
                # (x1, y1), (x2, y2), (x, y)
                顶点x, 顶点y = 点[5], 点[6]
                路径点列表.append((点[0], 点[1] * x, 点[2] * y, 点[3] * x, 点[4] * y, 点[5] * x, 点[6] * y))

            elif 点[0] in ["H", "h"]:
                顶点x = 点[1]
                路径点列表.append((点[0], 点[1] * x))
            elif 点[0] in ["V", "v"]:
                顶点y = 点[1]
                路径点列表.append((点[0], 点[1] * y))
            elif 点[0] in ["Q", "q", "S", "s"]:
                # (x1, y1), (x, y)
                顶点x, 顶点y = 点[3], 点[4]
                路径点列表.append((点[0], 点[1] * x, 点[2] * y, 点[3] * x, 点[4] * y))
            elif 点[0] in ["A", "a"]:
                # (rx, ry), x_axis_rotation, (large_arc_flag, sweep_flag), (x, y) =
                顶点x, 顶点y = 点[6], 点[7]
                路径点列表.append((点[0], 点[1] * x, 点[2] * y, 点[3], 点[4], 点[5], 点[6] * x, 点[7] * y))
            elif 点[0] in ["Z", "z"]:
                路径点列表.append((点[0],))
            else:
                顶点x, 顶点y = 点[0], 点[1]
                路径点列表.append((点[0] * x, 点[1] * y))

        self.设置属性值("点列表", 路径点列表)

        return self

    def 获取上一个点的位置(self):
        P = self.获取属性值
        点列表 = P("点列表")
        if len(点列表) == 0:
            return 0, 0
        last_x = None
        last_y = None
        for i in range(-1, -len(点列表), -1):
            倒数第二点 = 点列表[i]
            x, y = self._获取终点的坐标(倒数第二点)
            if x is not None:
                last_x = x
            if y is not None:
                last_y = y
            if last_x is not None and last_y is not None:
                break

        return last_x, last_y

    def 列表转方法调用(self):
        P = self.获取属性值
        点列表 = P("点列表")
        方法字典 = {
            "移动": self.移动到,
            "移动到": self.移动到,
            "平滑曲线": self.平滑曲线,
            "曲线": self.画曲线,
            "直线": self.画直线,
            "水平直线": self.画水平线,
            "水平线": self.画水平线,
            "垂直直线": self.画垂直线,
            "垂直线": self.画垂直线,
            "二次贝塞尔曲线": self.二次贝塞尔曲线,
            "平滑的二次贝塞尔曲线": self.平滑的二次贝塞尔曲线,
            "画圆弧": self.画圆弧
        }
        方法调用 = ""
        for 点命令, *参数 in 点列表:
            方法 = 方法字典.get(点命令, None)
            if 方法 is None:
                方法 = f".{点命令}()"
            else:
                params = self._get_function_params(方法)
                方法 = f". \\\n{方法.__name__}("
                pi = 0
                for name, param in params.items():
                    if "tuple" in str(param.annotation):
                        方法 += f"{name}=({参数[pi]}, {参数[pi + 1]}),"
                        pi += 1
                    else:
                        方法 += f"{name}={参数[pi]},"
                    pi += 1

            方法调用 += 方法.strip(",") + ")"
        print(方法调用)
        return self

    def _get_function_params(self, function):
        import inspect

        sig = inspect.signature(function)
        params = sig.parameters
        return params

    def 追加顶点(self, 顶点命令):
        P = self.获取属性值
        点列表 = P("点列表")
        点列表.append(顶点命令)
        return self

    def 合并路径(self, 路径元素_, 保留=否):
        元素_: 路径元素 = 路径元素_
        合并点列表 = 元素_.获取属性值("点列表")
        P = self.获取属性值
        现有点列表 = P("点列表")
        self.设置属性值("点列表", 现有点列表 + 合并点列表)
        if not 保留:
            元素_.所属组.remove(元素_)
        return self

    def 中心点计算(self, 元素位置: 元素位置信息):
        from 神笔码靓.神笔库 import 多边形工具
        现有点列表 = self.获取路径点列表()
        # 元素位置.中心点 = 多边形工具.获取中心点(现有点列表)

        xmin, xmax, ymin, ymax = self.获取边界框()
        元素位置.中心点 = (xmin + xmax) / 2, (ymin + ymax) / 2
        return 元素位置.中心点

    @property
    def 路径点列表(self):
        P = self.获取属性值
        点列表 = P("点列表")
        return 点列表

    def 原生路径修正(self):
        self.设置属性值("原生路径修正", True)
        return self

    def 原生path(self, d):
        self.设置属性值(属性名="原生path", 属性值=d)
        return self

    def 追加顶点列表(self, 追加顶点列表):
        P = self.获取属性值
        点列表 = P("点列表")
        点列表 += 追加顶点列表
        return self

    def 获取元素范围(self):
        return self.获取边界框()
        # return self.获取路径点列表范围()

    def 路径并集(self, 路径):
        if 路径 == self:
            return self

        raise NotImplementedError("暂不支持路径并集")

        return self

    def 路径交集(self, 路径):
        if 路径 == self:
            return self
        raise NotImplementedError("暂不支持路径交集")
        from svgpathtools import Path, parse_path
        if isinstance(路径, str):
            path2_str = 路径
        elif isinstance(路径, 路径元素):
            path2_str = 路径.获取路径d()
        path1_str = self.获取路径d()
        #
        # path1_obj.unite() # unite()和intersect()
        path1_obj = Path(path1_str).intersect(path2_str)
        # Path.unit_tangent()
        self.设置属性值("原生path", path1_obj.d())
        return self

    def 路径差集(self, 路径):
        if 路径 == self:
            return self
        from svgpathtools import Path
        raise NotImplementedError("暂不支持路径差集")

        return self

    # def 路径并集(self):
    #     from svgpathtools import *
    #     pass

    def 平滑路径(self, 最大关节大小=3, 紧致度=1.99, 忽略无法修正的弯曲=False):
        平滑路径参数 = {
            "maxjointsize": 最大关节大小,
            "tightness": 紧致度,
            "ignore_unfixable_kinks": 忽略无法修正的弯曲
        }
        self.设置属性值("平滑路径", 平滑路径参数)

    def 获取边界框(self):
        from svgpathtools import parse_path
        P = self.获取属性值
        原生path = P("原生path", None)
        if 原生path is None:
            点列表 = P("点列表")
            点列表 = 神笔绘图板工具.转SVG绘制路径命令(点列表)
            # d_list = 神笔绘图板工具.点列表转SVG路径(点列表)
            path = " ".join(神笔绘图板工具.点列表转SVG路径(点列表))
        else:
            path = 原生path
        path_alt = parse_path(path)
        xmin, xmax, ymin, ymax = path_alt.bbox()
        return xmin, xmax, ymin, ymax

    def 获取路径d(self):
        P = self.获取属性值
        原生path = P("原生path", None)
        if 原生path is not None:
            return 原生path

        点列表 = P("点列表")
        点列表 = 神笔绘图板工具.转SVG绘制路径命令(点列表)
        d_list = 神笔绘图板工具.点列表转SVG路径(点列表)
        return " ".join(d_list)

    def 构建SVG元素(self):
        P = self.获取属性值
        C = self.获取颜色值
        平滑路径参数 = P("平滑路径")

        原生path = P("原生path", None)
        # P("文字", None)
        if 原生path is None:
            点列表 = P("点列表")
            点列表 = 神笔绘图板工具.转SVG绘制路径命令(点列表)

            if len(点列表) == 0:
                path = self.dwg.path(d="M0,0 L0,0 z",
                                     # stroke=C("描边颜色"),
                                     # fill=C("填充颜色"),
                                     # stroke_width=P("描边宽度"),
                                     # opacity=P("透明度", 1),
                                     shape_rendering='auto',
                                     # stroke_linejoin="arcs",
                                     id=self.获取元素id(t="p"))

                样式可修改元素.属性修改处理(self, path)
                可变换元素.属性修改处理(self, path)
                return path

            fp = 点列表[0]
            if fp[0] in ["M", "m"]:
                t, x, y = fp
            elif len(fp) == 2:
                t, x, y = "M", fp[0], fp[1]
            # elif str(fp[0])[0] in ['M', 'm', 'L', 'l', 'H', 'h', 'V', 'v', 'C', 'c', 'S', 's', 'Q', 'q', 'T', 't', 'A',
            #                        'a',
            #                        'Z', 'z']:
            #     if len(fp) == 3:
            #         t, x, y = fp[0], float(fp[1]), float(fp[2])
            #     else:
            #         # TODO: 原因未知
            #         fp = str(fp[0])
            #         d = fp[1:].split(",")
            #         t, x, y = fp[0], float(d[0]), float(d[1])
            else:
                # 如果第一命令不是移动，则默认移动到 0,0
                t, x, y = "M", 0, 0
                点列表 = [(t, x, y)] + 点列表

            path = self.dwg.path(d="{}{:.2f},{:.2f} ".format(t, float(x), float(y)),
                                 # stroke=C("描边颜色"),
                                 # fill=C("填充颜色"),
                                 # stroke_width=P("描边宽度"),
                                 # opacity=P("透明度", 1),
                                 shape_rendering='auto',
                                 # stroke_linejoin="arcs",
                                 id=self.获取元素id(t="p"))

            d_list = 神笔绘图板工具.点列表转SVG路径(点列表)
            # if 转相对坐标:
            #     d_list = 神笔绘图板工具.绝对坐标转相对坐标(d_list)
            for di in d_list[1:]:
                if di[0] == "z":
                    path.push('Z')
                    continue
                path.push(di)

            if P("闭合", 否):
                path.push('Z')

            if 平滑路径参数 is not None:
                from svgpathtools import smoothed_path, parse_path
                path_d_str = " ".join(path.commands)
                # 平滑路径参数["path"] = path_d
                path_obj = parse_path(path_d_str)
                path_d = smoothed_path(path=path_obj, **平滑路径参数).d()
                path = self.dwg.path(d=path_d,
                                     # stroke=C("描边颜色"),
                                     # fill=C("填充颜色"),
                                     # stroke_width=P("描边宽度"),
                                     # opacity=P("透明度", 1),
                                     shape_rendering='auto',
                                     # stroke_linejoin="arcs",
                                     id=self.获取元素id(t="p"))

        else:
            from 神笔码靓.神笔库.路径工具集 import 修正SvgPath

            if P("原生路径修正", False):
                原生path = 修正SvgPath(原生path)

            if 平滑路径参数 is not None:
                from svgpathtools import smoothed_path
                原生path = smoothed_path(path=原生path, *平滑路径参数)

            path = self.dwg.path(d=原生path,
                                 # stroke_width=P("描边宽度"),
                                 shape_rendering='auto',
                                 id=self.获取元素id(t="p"))

            点列表 = P("点列表")
            if 点列表 is not None:
                if len(点列表) > 0:
                    点列表 = 神笔绘图板工具.转SVG绘制路径命令(点列表)
                    fp = 点列表[0]
                    if fp[0] in ["M", "m"]:
                        t, x, y = fp
                    elif len(fp) == 2:
                        t, x, y = "M", fp[0], fp[1]
                    else:
                        # 如果第一命令不是移动，则默认移动到 0,0
                        t, x, y = "M", 0, 0
                        点列表 = [(t, x, y)] + 点列表
                    d_list = 神笔绘图板工具.点列表转SVG路径(点列表)
                    # if 转相对坐标:
                    #     d_list = 神笔绘图板工具.绝对坐标转相对坐标(d_list)
                    for di in d_list[1:]:
                        if di[0] == "z":
                            path.push('Z')
                            continue
                        path.push(di)

                    if P("闭合", 否):
                        path.push('Z')
        # 不支持
        # path["vector-effect"] = "non-scaling-stroke"

        样式可修改元素.属性修改处理(self, path)
        可变换元素.属性修改处理(self, path)

        return path


def 路径合并测试():
    from 神笔码靓.神笔库 import 神笔绘图板

    神笔 = 神笔绘图板(作品文件路径="路径合并.svg")
    y = 神笔.高 / 2
    圆 = 神笔.符号.圆(中心点=(神笔.宽 / 2, y + 2), 半径=6)
    神笔.符号.叉(中心点=(神笔.宽 / 2, y + 18), 线长=12).合并路径(圆, 是).落笔(100, 100). \
        画直线(50, 50).画水平线(200).画垂直线(300).平滑的二次贝塞尔曲线((100, 100)).平滑的二次贝塞尔曲线((90, 200))
    神笔.完成()


def 偏转直行():
    from 神笔码靓.神笔库 import 神笔绘图板
    神笔 = 神笔绘图板(作品文件路径="偏转直行.svg")
    路径 = 神笔.路径(id_="路径").移动到(300, 100).画垂直线(200).画直线(400, 200).画垂直线(400).画水平线(100).画直线(300, 300)
    for i in range(10):
        路径.右转直线(90, 20).左转直线(90, 20)

    路径.画垂直线(100).右转直线(45, 20).左转直线(45, 20).画直线(100, 500)  # .画圆((0,0),100)

    for i in range(10):
        路径.右转直线(-140, 20).左转直线(-140, 20)

    路径.新描边样式("3,3", id_="新路径").画直线(100, 100).寻找路径(id_="路径").画直线(0, 0)
    神笔.完成()


def 偏转直行画放射线():
    from 神笔码靓.神笔库 import 神笔绘图板
    神笔 = 神笔绘图板(作品文件路径="偏转直行画放射线.svg")
    路径 = 神笔.路径(id_="路径")

    for i in range(0, 180):
        路径 = 路径.新路径().描边宽度(0.5).移动到(300, 300).右转直线(i * 2, 50).描边颜色(颜色.随机色()).右转直线(10, 10).右转直线(-10, 10)
    神笔.完成()


def 综合测试():
    from 神笔码靓.神笔库 import 神笔绘图板
    绘图板 = 神笔绘图板(作品文件路径="路径综合测试.svg")
    A = (100, 200)
    B = (400, 200)
    绘图板.路径().点辅助开关(开).移动到(A).画直线(B).点辅助(开启=否).平滑曲线(终点控制点=(110, 90), 终点=(110, 220)).前进直线(100).点辅助(开启=是) \
        .画垂直线(400).点辅助(开启=是).画曲线(开始控制点=(110, 500), 终点控制点=(32, 500), 终点=(45, 600)).点辅助(开启=是).前进直线(20).点辅助(开启=是) \
        .二次贝塞尔曲线(曲线控制点=(100, 800), 终点=(200, 500)).点辅助(开启=是) \
        .平滑的二次贝塞尔曲线(终点=(300, 200)).点辅助(开启=是).平滑的二次贝塞尔曲线(终点=(320, 180)).点辅助(开启=是) \
        .右转直线(6, 120).点辅助(开启=是).画圆弧(3, (450, 104), 圆弧方向=1).前进直线(20)
    绘图板.完成()


def 画圆弧求斜率测试():
    from 神笔码靓.神笔库 import 神笔绘图板
    绘图板 = 神笔绘图板(作品文件路径="画圆弧求斜率测试.svg")
    A = (100, 200)
    B = (400, 200)
    绘图板.路径().点辅助开关(开).移动到(A).画直线(B).画圆弧(200, (400, 400), 圆弧方向=0).前进直线(150)

    绘图板.完成()


if __name__ == '__main__':
    # 路径合并测试()
    # 偏转直行()
    # 综合测试()
    画圆弧求斜率测试()
